import { ref, watch } from "vue";
import { ComboListProps, Option } from "../combo-list.props";
import { UseDataSource } from "./types";

export function useDataSource(props: ComboListProps): UseDataSource {
    const displayText = ref<string>('');
    const modelValue = ref(props.modelValue);
    const dataSource = ref(props.data || []);
    const editable = ref(props.editable);

    function getItemsByValue(value: string): Option[] {
        const valueArray = value.split(props.separator).map<[string, boolean]>((valueText: string) => {
            return [valueText, true];
        });
        const valueMap = new Map<string, boolean>(valueArray);
        return dataSource.value.filter((item: Option) => valueMap.has(item[props.valueField]));
    }

    function updateDisplayTextByValue(value: string) {
        const matchedValue = getItemsByValue(value).map((item: Option) => item[props.textField]).join(props.separator);
        displayText.value = editable.value ? (matchedValue || value) : matchedValue;
    }

    function getItemsByDisplayText(text: string): Option[] {
        const displayTextArray = text.split(props.separator).map<[string, boolean]>((optionText: string) => {
            return [optionText, true];
        });
        const displayTextMap = new Map<string, boolean>(displayTextArray);
        return dataSource.value.filter((item: Option) => displayTextMap.has(item[props.textField]));
    }

    function buildSelectedItemByDisplayText(displayText: string) {
        const changedValue = {} as Record<string, string>;
        changedValue[props.idField] = displayText;
        changedValue[props.textField] = displayText;
        return [changedValue];
    }

    function getSelectedItemsByDisplayText(displayText: string) {
        let selectedItems = getItemsByDisplayText(displayText);
        const hasMatchedItems = selectedItems && selectedItems.length > 0;
        if (editable.value && !hasMatchedItems) {
            selectedItems = buildSelectedItemByDisplayText(displayText);
        }
        return selectedItems;
    }

    function loadRemoteData() {
        const { url, method = 'GET', headers = {}, body = null } = props.remote;
        const requestInfo = { method, headers, body };
        fetch(url, requestInfo)
            .then((response) => {
                if (response.status === 200) {
                    return response.json();
                }
                const error = new Error(response.statusText);
                throw error;
            })
            .then((data) => {
                dataSource.value = data;
            })
            .catch((err) => {
                console.error(err);
            });
    }

    if (props.remote) {
        loadRemoteData();
    }

    watch(() => props.data, () => {
        dataSource.value = props.data;
    });

    watch([dataSource], ([dataSourceValue]) => {
        if (props.modelValue) {
            const currentItem = dataSourceValue.find((item: any) => item[props.valueField] === props.modelValue);
            if (currentItem) {
                displayText.value = currentItem[props.textField];
            }
        }
    });

    watch(() => props.modelValue, (newValue) => {
        modelValue.value = newValue;
        updateDisplayTextByValue(newValue);
    });

    updateDisplayTextByValue(props.modelValue);

    return { dataSource, displayText, editable, modelValue, getItemsByDisplayText, getItemsByValue, getSelectedItemsByDisplayText };
}
